package cn.trigram.mgb.simple;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

import com.google.common.collect.Lists;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.SimpleXMLMapperGenerator;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.elements.AbstractXmlElementGenerator;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.StringUtility;

public class SimpleXMLMapperGeneratorImpl extends SimpleXMLMapperGenerator {

  @Override
  protected void addSelectByPrimaryKeyElement(XmlElement parentElement) {

    AbstractXmlElementGenerator elementGenerator = new AbstractXmlElementGenerator() {

      @Override
      public void addElements(XmlElement parentElement) {

        XmlElement answer = new XmlElement("select"); //$NON-NLS-1$

        answer.addAttribute(new Attribute(
            "id", introspectedTable.getSelectByPrimaryKeyStatementId())); //$NON-NLS-1$
        answer.addAttribute(new Attribute(
            "resultMap", //$NON-NLS-1$
            introspectedTable.getBaseResultMapId()
        ));

        String parameterType;
        // PK fields are in the base class. If more than on PK
        // field, then they are coming in a map.
        if (introspectedTable.getPrimaryKeyColumns().size() > 1) {
          parameterType = "map"; //$NON-NLS-1$
        } else if (introspectedTable.getPrimaryKeyColumns().size() == 1) {
          parameterType =
              introspectedTable.getPrimaryKeyColumns().get(0).getFullyQualifiedJavaType().toString();
        } else {
          parameterType = "string";
        }

        answer.addAttribute(new Attribute("parameterType", parameterType)); //$NON-NLS-1$

        context.getCommentGenerator().addComment(answer);

        StringBuilder sb = new StringBuilder();
        sb.append("select "); //$NON-NLS-1$

        if (stringHasValue(introspectedTable.getSelectByPrimaryKeyQueryId())) {
          sb.append('\'');
          sb.append(introspectedTable.getSelectByPrimaryKeyQueryId());
          sb.append("' as QUERYID,"); //$NON-NLS-1$
        }

        buildSelectList(sb.toString(), introspectedTable.getAllColumns()).forEach(answer::addElement);

        sb.setLength(0);
        sb.append("from "); //$NON-NLS-1$
        final String fullTableName = Lists.newArrayList(
                introspectedTable.getFullyQualifiedTable().getIntrospectedSchema(),
                introspectedTable.getFullyQualifiedTable().getAliasedFullyQualifiedTableNameAtRuntime()
            )
            .stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("."));
        sb.append(fullTableName);
        answer.addElement(new TextElement(sb.toString()));

        buildPrimaryKeyWhereClause().forEach(answer::addElement);

        if (context.getPlugins().sqlMapSelectByPrimaryKeyElementGenerated(answer, introspectedTable)) {
          parentElement.addElement(answer);
        }
      }
    };
    initializeAndExecuteGenerator(elementGenerator, parentElement);
  }

  @Override
  protected void addSelectAllElement(XmlElement parentElement) {

    AbstractXmlElementGenerator elementGenerator = new AbstractXmlElementGenerator() {

      @Override
      public void addElements(XmlElement parentElement) {

        XmlElement answer = new XmlElement("select"); //$NON-NLS-1$

        answer.addAttribute(new Attribute(
            "id", introspectedTable.getSelectAllStatementId())); //$NON-NLS-1$
        answer.addAttribute(new Attribute(
            "resultMap", //$NON-NLS-1$
            introspectedTable.getBaseResultMapId()
        ));

        context.getCommentGenerator().addComment(answer);

        buildSelectList("select ", introspectedTable.getAllColumns()).forEach(answer::addElement); //$NON-NLS-1$

        StringBuilder sb = new StringBuilder();
        sb.append("from "); //$NON-NLS-1$
        final String fullTableName = Lists.newArrayList(
                introspectedTable.getFullyQualifiedTable().getIntrospectedSchema(),
                introspectedTable.getFullyQualifiedTable().getAliasedFullyQualifiedTableNameAtRuntime()
            )
            .stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("."));
        sb.append(fullTableName);
        answer.addElement(new TextElement(sb.toString()));

        String orderByClause = introspectedTable.getTableConfigurationProperty(
            PropertyRegistry.TABLE_SELECT_ALL_ORDER_BY_CLAUSE);
        boolean hasOrderBy = StringUtility.stringHasValue(orderByClause);
        if (hasOrderBy) {
          sb.setLength(0);
          sb.append("order by "); //$NON-NLS-1$
          sb.append(orderByClause);
          answer.addElement(new TextElement(sb.toString()));
        }

        if (context.getPlugins().sqlMapSelectAllElementGenerated(answer, introspectedTable)) {
          parentElement.addElement(answer);
        }
      }
    };
    initializeAndExecuteGenerator(elementGenerator, parentElement);
  }

  @Override
  protected void addInsertElement(XmlElement parentElement) {

    if (introspectedTable.getRules().generateInsert()) {
      AbstractXmlElementGenerator elementGenerator = new AbstractXmlElementGenerator() {

        @Override
        public void addElements(XmlElement parentElement) {

          FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(
              introspectedTable.getBaseRecordType());

          XmlElement insertElement = buildInitialInsert(
              introspectedTable.getInsertStatementId(), parameterType);
          final String fullTableName = Lists.newArrayList(
                  introspectedTable.getFullyQualifiedTable().getIntrospectedSchema(),
                  introspectedTable.getFullyQualifiedTable().getFullyQualifiedTableNameAtRuntime()
              )
              .stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("."));
          insertElement.addElement(new TextElement(
              String.format("insert into %s (", fullTableName)));

          final XmlElement columnTrimElement = new XmlElement("trim");
          insertElement.addElement(columnTrimElement);
          columnTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
          insertElement.addElement(new TextElement(") values ("));
          final XmlElement valuesTrimElement = new XmlElement("trim");
          insertElement.addElement(valuesTrimElement);
          valuesTrimElement.addAttribute(new Attribute("suffixOverrides", ","));
          insertElement.addElement(new TextElement(")"));

          List<IntrospectedColumn> columns =
              ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
          for (IntrospectedColumn introspectedColumn : columns) {
            final XmlElement columnTrimIfElement = new XmlElement("if");
            columnTrimElement.addElement(columnTrimIfElement);
            columnTrimIfElement.addAttribute(
                new Attribute("test", String.format("%s != null", introspectedColumn.getJavaProperty())));
            columnTrimIfElement.addElement(new TextElement(String.format(
                "%s,",
                MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn)
            )));
            final XmlElement valuesTrimIfElement = new XmlElement("if");
            valuesTrimElement.addElement(valuesTrimIfElement);
            valuesTrimIfElement.addAttribute(
                new Attribute("test", String.format("%s != null", introspectedColumn.getJavaProperty())));
            valuesTrimIfElement.addElement(new TextElement(
                String.format("%s,", MyBatis3FormattingUtilities.getParameterClause(introspectedColumn))));
          }

          if (context.getPlugins().sqlMapInsertElementGenerated(insertElement, introspectedTable)) {
            parentElement.addElement(insertElement);
          }
        }
      };
      initializeAndExecuteGenerator(elementGenerator, parentElement);
    }
  }

  @Override
  protected void addUpdateByPrimaryKeyElement(XmlElement parentElement) {

    if (introspectedTable.getRules().generateUpdateByPrimaryKeySelective()) {
      AbstractXmlElementGenerator elementGenerator = new AbstractXmlElementGenerator() {

        @Override
        public void addElements(XmlElement parentElement) {

          List<IntrospectedColumn> columns       = introspectedTable.getNonPrimaryKeyColumns();
          final String             statementId   = introspectedTable.getUpdateByPrimaryKeyStatementId();
          final String             parameterType = introspectedTable.getBaseRecordType();
          XmlElement               updateElement = new XmlElement("update");

          updateElement.addAttribute(new Attribute("id", statementId));
          updateElement.addAttribute(new Attribute("parameterType", parameterType));

          context.getCommentGenerator().addComment(updateElement);
          final String fullTableName = Lists.newArrayList(
                  introspectedTable.getFullyQualifiedTable().getIntrospectedSchema(),
                  introspectedTable.getFullyQualifiedTable().getFullyQualifiedTableNameAtRuntime()
              )
              .stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("."));
          updateElement.addElement(
              new TextElement("update " + fullTableName));

          //set 标签
          XmlElement setElement = new XmlElement("set");
          updateElement.addElement(setElement);

          for (IntrospectedColumn introspectedColumn : ListUtilities.removeGeneratedAlwaysColumns(columns)) {
            XmlElement ifElement = new XmlElement("if");
            setElement.addElement(ifElement);
            ifElement.addAttribute(
                new Attribute("test", String.format("%s != null", introspectedColumn.getJavaProperty())));
            ifElement.addElement(new TextElement(String.format(
                "%s = %s,",
                MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn),
                MyBatis3FormattingUtilities.getParameterClause(introspectedColumn)
            )));
          }

          buildPrimaryKeyWhereClause().forEach(updateElement::addElement);

          if (context.getPlugins()
              .sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(updateElement, introspectedTable)) {
            parentElement.addElement(updateElement);
          }
        }
      };
      initializeAndExecuteGenerator(elementGenerator, parentElement);
    }
  }

  @Override
  protected void addDeleteByPrimaryKeyElement(XmlElement parentElement) {

    AbstractXmlElementGenerator elementGenerator = new AbstractXmlElementGenerator() {

      @Override
      public void addElements(XmlElement parentElement) {

        XmlElement answer = new XmlElement("delete"); //$NON-NLS-1$

        answer.addAttribute(new Attribute("id", introspectedTable.getDeleteByPrimaryKeyStatementId())); //$NON-NLS-1$
        String parameterClass;
        // PK fields are in the base class. If more than on PK
        // field, then they are coming in a map.
        if (introspectedTable.getPrimaryKeyColumns().size() > 1) {
          parameterClass = "map"; //$NON-NLS-1$
        } else if (introspectedTable.getPrimaryKeyColumns().size() == 1) {
          parameterClass =
              introspectedTable.getPrimaryKeyColumns().get(0).getFullyQualifiedJavaType().toString();
        } else {
          parameterClass = "string";
        }
        answer.addAttribute(new Attribute("parameterType", parameterClass)); //$NON-NLS-1$

        context.getCommentGenerator().addComment(answer);

        final String fullTableName = Lists.newArrayList(
                introspectedTable.getFullyQualifiedTable().getIntrospectedSchema(),
                introspectedTable.getFullyQualifiedTable().getFullyQualifiedTableNameAtRuntime()
            )
            .stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("."));
        String sb = "delete from " + fullTableName; //$NON-NLS-1$
        answer.addElement(new TextElement(sb));

        buildPrimaryKeyWhereClause().forEach(answer::addElement);

        if (context.getPlugins().sqlMapDeleteByPrimaryKeyElementGenerated(answer, introspectedTable)) {
          parentElement.addElement(answer);
        }
      }
    };
    initializeAndExecuteGenerator(elementGenerator, parentElement);
  }

}
